/**
*    This file is part of Basic Service.
*
*    Basic Service is free software: you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    Basic Service is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with Basic Service (See GPL.txt).  If not, see <http://www.gnu.org/licenses/>.
*    
* 	If needed the author is Amir Zucker and can be reached at amirzucker1@gmail.com or amirzucker.wordpress.com
*/
package com.basicservice.service;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.owasp.appsensor.errors.AppSensorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.basicservice.domain.User;
import com.basicservice.dto.validators.UUIDValidatedString;

public class RequestInterceptor extends HandlerInterceptorAdapter {
	@Autowired
	protected UserService userService;
	@Autowired
	protected SecurityService securityService;
	
	private static final Logger LOG = LoggerFactory
			.getLogger(RequestInterceptor.class);

	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		try {
			boolean csrfOK = handleCSRF(request, response);
			if (csrfOK) {
				handleUserLogin(request);
				return true;
			} else {
				return false;
			}
		} catch (Exception e) {
			LOG.debug("request csrf check failed with an exception", e);
			response.setStatus(418);
			return false;
		}
	}

	private void handleUserLogin(HttpServletRequest request) {
		boolean invalidateSession = true;
		String authenticatedSessionId = Utils.findCookie(request, Constants.AUTHENTICATED_SESSION_ID_COOKIE);
		if (authenticatedSessionId != null) {
			try {
				UUIDValidatedString validatedSessionId = new UUIDValidatedString();
				validatedSessionId.setSecurityService(securityService);
				validatedSessionId.setValue(authenticatedSessionId);
				if (validatedSessionId.validateValue() && validatedSessionId.getValue() != null) {
					User user = userService.getUserBySessionId(validatedSessionId.getValue());
					if (user != null) {
						request.setAttribute(Constants.USER_ATTRIBUTE, user);
						securityService.signInUserToSecurityContext(user);
						invalidateSession = false;
					}
				}
			} catch (Exception e) {
				LOG.debug("Exception caught while setting user to request", e);
			}
		}
		if (invalidateSession) {
			securityService.signoutUser();
		}
	}

	/**
	 * @param request
	 * @param response
	 * @return
	 */
	public boolean handleCSRF(HttpServletRequest request,
			HttpServletResponse response) {
		String csrfValueFromHeader = request.getHeader(Constants.CSRF_TRANSPORT_NAME);
		String csrfValueFromCookie = Utils.findCookie(request, Constants.CSRF_TRANSPORT_NAME);
		boolean validationResult = 
				csrfValueFromCookie != null && // make sure it has a value
				!csrfValueFromCookie.isEmpty() && // make sure it's not the empty value we put in there
				csrfValueFromCookie.equals(csrfValueFromHeader); // make sure it matches the header
		request.setAttribute(Constants.CSRF_SECURE, validationResult);
		String method = request.getMethod().toLowerCase();
		if (
				Constants.csrfValidatedMethods.contains(method) && // we need to protect this call
				!validationResult // validation failed
				) {
			LOG.info("[CSRF]: Validation failed for request: " + request.getRequestURI());
			Cookie[] cookies = request.getCookies();
			StringBuffer sb = new StringBuffer();
			for (Cookie oneCookie : cookies) {
				sb.append("\nCookie name: ").append(oneCookie.getName())
					.append("\nCookie value: ").append(oneCookie.getValue())
					.append("\nCookie path: ").append(oneCookie.getPath())
					.append("\nCookie maxage: ").append(oneCookie.getMaxAge());
			}
			LOG.debug("[CSRF]: Cookies: " + sb.toString());
			LOG.debug("[CSRF]: CSRF header: " + csrfValueFromHeader);
			LOG.debug("[CSRF]: CSRF cookie: " + csrfValueFromCookie);
			try {
				new AppSensorException("IE4", "CSRF validation failure", "CSRF validation failure");
			} catch (Exception e) {
				// AppSensor might throw an exception, but we want to catch it here and stop propagation
			}
			response.setStatus(418);
			return false;
		}
		return true;
	}
	
	@Override
	public void postHandle(
			HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
			throws Exception {
		// once we're done reading this cookie, delete it in the response so it can't be reused.
		Cookie cookie = new Cookie(Constants.CSRF_TRANSPORT_NAME, "");
		cookie.setPath("/");
		cookie.setMaxAge(0);
		response.addCookie(cookie);
	}
}